Check box example: caption options

This example shows one way to use check boxes in a nondialog window. It also shows you what happens when these caption options are combined. It shows how to change a window style after it has been created. And, finally, it shows how to force the redrawing of a window.

First we take care of the main window, and the application start and end.

    .data
mainwnd dd 0
wc WNDCLASSEX <size WNDCLASSEX,CS_HREDRAW+CS_VREDRAW,WndProc,0,0, 0, \
                0,0,COLOR_WINDOW+1, 0,wndclsname,0>
mainbox CREATEARGS <0,wndclsname,caption,WS_OVERLAPPEDWINDOW+WS_VISIBLE,\
                    40,40,300,110,0,0,0,0>
wndclsname  db 'generic',0
caption  db 'Caption (title bar) styles',0

        public  InitApp,EndApp
        extrn   LoadCursor:near

        .code
InitApp:
        mov     edi,offset wc
        mov     esi,offset mainbox

        push    large IDC_ARROW
        push    large 0
        call    LoadCursor
        mov     [wc].wcxCursor,eax
        mov     [wc2].wcxCursor,eax
        ret
;
; Application cleanup subroutine
; Returns:
;       EAX = application exit code
;
EndApp:
        xor     eax,eax ; assume no errors
        ret
Next we define our test window. We explicitly add in the WS_CAPTION because we will use a copy of this style argument as a basis for style changes. It turns out that although an overlapped windows always creates a caption (title bar), the title bar can be removed after the window is created by changing the window style.
    The window procedure for this window is DefWindowProc because no special message handling is required.
        .data
        align 4
testwnd dd 0
wc2 WNDCLASSEX <size WNDCLASSEX,CS_HREDRAW+CS_VREDRAW,DefWindowProc,0,0, \
             0, 0,0,COLOR_WINDOW+1, 0,wndclsname2,0>
test    CREATEARGS <0,wndclsname2,caption2,WS_OVERLAPPED+WS_CAPTION+\
                    WS_VISIBLE, 160,160,200,200,0,0,0,0>
wndclsname2 db 'testdisplay',0
caption2 db 'Display test',0
Now we define the check boxes, which are defined as buttons. There is no WNDCLASSEX structure because the button class is already defined and registered. We will handle all of the important check box activity in the main window, which is the parent (and container) of all these check boxes.
        align 4
test_style dd 0

; Button control IDs

IDCTL_SYSMENU equ 101
IDCTL_SIZEBOX equ 102
IDCTL_MINBOX  equ 103
IDCTL_MAXBOX  equ 104

; Button control

chkbox  CREATEARGS <0,btnclsname,0,WS_CHILD+WS_VISIBLE+BS_CHECKBOX,\
                    0,0,0,0, 0,0,0,0>
btnclsname  db 'button',0       ; letter case doesn't matter

; Button style/initialization tables
    align 4
sysmenu dd WS_SYSMENU,sysmenu_str,10,10,250,15,IDCTL_SYSMENU
sizebox dd WS_THICKFRAME,sizebox_str,10,25,250,15,IDCTL_SIZEBOX
minbox  dd WS_MINIMIZEBOX,minbox_str,10,40,250,15,IDCTL_MINBOX
maxbox  dd WS_MAXIMIZEBOX,maxbox_str,10,55,250,15,IDCTL_MAXBOX

sysmenu_str db 'WS_SYSMENU',0
sizebox_str db 'WS_THICKFRAME/WM_SIZEBOX',0
minbox_str  db 'WS_MINIMIZEBOX',0
maxbox_str  db 'WS_MAXIMIZEBOX',0
There is only one window procedure defined by us, and that is the one for the main window, the one with the check boxes in it. We start with the message dispatch.
        extrn   DefWindowProc:near

        .code
WndProc:
        mov     eax,[esp+4+4]           ; message ID
        cmp     eax,WM_COMMAND          ; controls clicked
        je      execute_command
        cmp     eax,WM_CREATE           ; window created
        je      finish_create
        cmp     eax,WM_DESTROY          ; about to start window destruction
        je      start_destroy
        jmp     DefWindowProc           ; delegate other message processing
We create the check boxes here, as a response to WM_CREATE. At this time, the main window has been created, and there is a window handle for it. The handle is used to assign the window as parent to each check box, which are created as a child window. A child window must be assigned a parent when it is created.
    We also register the test window class here, and create the test window.
        extrn   CreateWindowEx:near
        extrn   RegisterClassEx:near

finish_create:
        mov     eax,[esp+4+0]  ; hwnd
        mov     [mainwnd],eax
        mov     eax,[wc].wcxInstance
        mov     [wc2].wcxInstance,eax
        push    offset wc2
        call    RegisterClassEx

        push    esi
        push    edi

        mov     esi,offset test
        mov     eax,[esi].cwargStyle
        mov     [test_style],eax                ; save original style
        call    install_subwindow
        mov     [testwnd],eax

        mov     esi,offset chkbox
        mov     edi,offset sysmenu
        call    install_chkbox

        mov     esi,offset chkbox
        mov     edi,offset sizebox
        call    install_chkbox

        mov     esi,offset chkbox
        mov     edi,offset minbox
        call    install_chkbox

        mov     esi,offset chkbox
        mov     edi,offset maxbox
        call    install_chkbox

        pop     edi
        pop     esi

        xor     eax,eax    ; signal a successful CREATE
        ret     16
;
; Create chkbox
;
;       ESI = address of CreateWindowEx arguments
;       EDI = chkbox table entry
;
; Returns:
;
;       EAX = BUTTON handle
;
install_chkbox:
        mov     eax,4[edi]
        mov     [chkbox].cwargName,eax  ; set window name = window text
        mov     eax,8[edi]
        mov     [chkbox].cwargX,eax
        mov     eax,12[edi]
        mov     [chkbox].cwargY,eax
        mov     eax,16[edi]
        mov     [chkbox].cwargCx,eax
        mov     eax,20[edi]
        mov     [chkbox].cwargCy,eax
        mov     eax,24[edi]
        mov     [chkbox].cwargMenu,eax  ; ctl ID when WS_CHILD
        call    install_subwindow
        ret
;
; Create main window's subwindows
;
;       ESI = address of CreateWindowEx arguments
;
; Returns:
;
;       EAX = handle of subwindow
;
install_subwindow:
        mov     eax,[mainwnd]
        mov     [esi].cwargParent,eax   ; make "mainbox" owner or parent
                                                ;   of new window
        mov     eax,[wc].wcxInstance    ; get instance
        mov     [esi].cwargInstance,eax ; set instance of window class
        sub     esp,48    ; allocate args
        mov     edi,esp
        mov     ecx,12
        rep movsd
        call    CreateWindowEx
        ret
;
; Process WM_DESTROY.  Sent after window is removed from screen, but
; before any destruction begins.
;
        extrn PostQuitMessage:near

start_destroy:
        push    large 0
        call    PostQuitMessage

        xor     eax,eax
        ret     16
This is where most of the work happens, in response to WM_COMMAND. If the command is not a button (check box) command, we quit. Otherwise we fetch the style option. Because the option is encoded in a single bit, the option doubles as a bit mask.
        extrn   SendMessage:near
        extrn   SetWindowLong:near
        extrn   RedrawWindow:near

execute_command:
        mov     eax,[esp+4+8]   ; wParam
        mov     edx,[esp+4+12]  ; lParam
        cmp     eax,(BN_CLICKED shl 16)+IDCTL_SYSMENU
        je      select_sysmenu
        cmp     eax,(BN_CLICKED shl 16)+IDCTL_SIZEBOX
        je      select_sizebox
        cmp     eax,(BN_CLICKED shl 16)+IDCTL_MINBOX
        je      select_minbox
        cmp     eax,(BN_CLICKED shl 16)+IDCTL_MAXBOX
        je      select_maxbox
        jmp     exit_execute_command
;
; Get styles from table
;
select_sysmenu:
        mov     ecx,[sysmenu]
        jmp     toggle
select_sizebox:
        mov     ecx,[sizebox]
        jmp     toggle
select_minbox:
        mov     ecx,[minbox]
        jmp     toggle
select_maxbox:
        mov     ecx,[maxbox]
        jmp     toggle
Now we get and reset the check box state. Here we see that messages are the main means of transferring information to and from the controls.
toggle:
        push    ecx             ; save style

        push    edx             ; save ctl handle

        push    large 0
        push    large 0
        push    large BM_GETCHECK
        push    edx             ; ctl handle from lParam
        call    SendMessage

        pop     edx             ; retrieve ctl handle

        xor     eax,1           ; toggle check state

        push    large 0
        push    eax             ; new check state
        push    large BM_SETCHECK
        push    edx             ; ctl handle
        call    SendMessage

        pop     ecx             ; retrieve style
Then we change the window style of our test window.
        xor     [test_style],ecx        ; toggle style bit
        push    [test_style]    ; new style
        push    large GWL_STYLE
        push    [testwnd]               ; test window
        call    SetWindowLong
Changing the window style does not cause a redrawing of the window. Without the following, the test window doesn't change until it is selected. And the redraw is imperfect.
    So we add this API call to force a redrawing of the test window without selecting it. The title bar is part of the "frame".
        push    large (RDW_FRAME+RDW_INVALIDATE+RDW_UPDATENOW)
        push    large 0         ; update rectangle
        push    large 0         ; update region (overrides rect)
        push    [testwnd]               ; test window
        call    RedrawWindow

exit_execute_command:
        xor     eax,eax    ; signal WM_COMMAND was processed
        ret     16